package com.xqboss.apps.service.consumer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.domain.consumer.ConsumerTeam;
import com.xqboss.apps.domain.consumer.ConsumerTeamGainRule;
import com.xqboss.apps.domain.consumer.ConsumerTeamUpgradePlan;
import com.xqboss.apps.domain.equipment.Equipment;
import com.xqboss.apps.domain.punch.PunchUserConfig;
import com.xqboss.apps.enums.consumer.ConsumerTeamMemberTypeEnum;
import com.xqboss.apps.enums.consumer.ConsumerTeamRoleEnum;
import com.xqboss.apps.enums.consumer.ConsumerTeamUpgradePlanTypeEnum;
import com.xqboss.apps.enums.consumer.InteractMsgEnum;
import com.xqboss.apps.enums.nft.UserNftStatusEnum;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeDirectionEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.enums.user.WalletWrenchChangeTypeEnum;
import com.xqboss.apps.service.equipment.EquipmentService;
import com.xqboss.apps.service.nft.NftService;
import com.xqboss.apps.service.punch.PunchUserConfigService;
import com.xqboss.apps.service.punch.PunchUserRecordService;
import com.xqboss.apps.service.punch.UserPunchService;
import com.xqboss.apps.service.user.SysUserNftService;
import com.xqboss.apps.service.user.SysUserWalletService;
import com.xqboss.apps.service.user.UserAssetWrenchRecordService;
import com.xqboss.apps.vo.consumer.*;
import com.xqboss.apps.vo.sys.SysUserTeamVo;
import com.xqboss.common.core.domain.R;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.SecurityUtils;
import com.xqboss.common.utils.bean.BeanUtils;
import com.xqboss.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 队伍升级-统一业务层
 *
 * @author makejava
 * @since 2023-12-15 14:27:10
 */
@Service
public class ConsumerTeamUpgradeService{

    @Autowired
    private ConsumerTeamGainRuleService consumerTeamGainRuleService;//队伍增益规则
    @Autowired
    private ConsumerTeamUpgradePlanService consumerTeamUpgradePlanService;//队伍升级方案
    @Autowired
    private ConsumerTeamService consumerTeamService;//队伍
    @Autowired
    private PunchUserRecordService punchUserRecordService;//用户-打卡记录
    @Autowired
    private ConsumerTeamMemberService consumerTeamMemberService;//队伍成员
    @Autowired
    private SysUserNftService sysUserNftService;//用户藏品
    @Autowired
    private NftService nftService;//藏品
    @Autowired
    private ISysConfigService sysConfigService;//系统配置参数
    @Autowired
    private SysUserWalletService sysUserWalletService;//消耗热豆
    @Autowired
    private UserAssetWrenchRecordService userAssetWrenchRecordService;//消耗扳手
    @Autowired
    private ConsumerTeamGainRuleService teamGainRuleService;//增益规则

    @Autowired
    private PunchUserConfigService punchUserConfigService;
    @Autowired
    private EquipmentService equipmentService;
    @Autowired
    private UserPunchService userPunchService;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;//自定义的线程池

    /**
     * 获取队伍详情
     *
     * @param teamId 队伍id
     * @param userId 用户id
     * @param isAsync 是否异步
     * @return 队伍详情
     */
    public R<ConsumerTeamDetailVo> teamDetail(Long teamId, Long userId, Boolean isAsync) {
        // 1.获取队伍成员信息
        ConsumerTeamDetailVo vo = consumerTeamService.getBaseMapper().getTeamMemberNew(teamId);
        if (ObjUtil.isNull(vo)) {
            return R.fail("未查询到队伍信息~");
        }

        // 2.验证队伍可否升级 、筛选出以主队员为首的对应家庭信息 、队伍打卡增益配置、队伍当前增益信息、队伍下阶段增益信息
        teamDetailInfo(teamId, userId, vo, isAsync);


        return R.ok(vo,"获取队伍详情成功！");
    }

    /**
     *  验证队伍可否升级 、筛选出以主队员为首的对应家庭信息 、队伍打卡增益配置、队伍当前增益信息、队伍下阶段增益信息
     *
     * @param teamId 队伍id
     * @param userId 用户id
     * @param vo vo
     * @param isAsync 是否异步
     */
    private void teamDetailInfo(Long teamId, Long userId, ConsumerTeamDetailVo vo,Boolean isAsync) {
        if (isAsync) {
            // 2.验证队伍可否升级
            CompletableFuture<Void> a = CompletableFuture.runAsync(() -> isUpgrade(teamId, userId, vo), threadPoolTaskExecutor);
            // 3 筛选出以主队员为首的对应家庭信息
            CompletableFuture<Void> b = CompletableFuture.runAsync(() -> filterTeamMembers(userId, vo), threadPoolTaskExecutor);
            // 4.获取 队伍打卡增益配置、队伍当前增益信息、队伍下阶段增益信息
            CompletableFuture<Void> c = CompletableFuture.runAsync(() -> obtainGain(teamId, userId, vo), threadPoolTaskExecutor);
            // 线程阻塞
            CompletableFuture.allOf(a,b,c).join();
        }else{
            // 2.验证队伍可否升级
            isUpgrade(teamId, userId, vo);

            // 3 筛选出以主队员为首的对应家庭信息
            filterTeamMembers(userId, vo);

            // 4.获取 队伍打卡增益配置、队伍当前增益信息、队伍下阶段增益信息
            obtainGain(teamId, userId, vo);
        }
    }


    /**
     * 队伍升级详情
     *
     * 1.验证队伍升级开关
     * 2.返回材料或普通升级详情
     *
     * @param userId 用户id
     * @return 队伍升级详情
     */
    public R<TeamUpgradesVo> upgradeInfo(Long teamId,Long userId) {
        //1.验证队伍升级开关
        Boolean isOpen = this.sysConfigService.getValue(Config.Sys.Team.升级开关);
        if (!isOpen) {
           return R.fail("升级维护中~");
        }
        //2.获取队伍信息
        ConsumerTeam team = this.consumerTeamService.getById(teamId);
        if (ObjUtil.isNull(team)) {
            return R.fail("未查询到队伍信息~");
        }
        //3.验证当前队伍等级是否为最高等级
        Integer maxRank = this.sysConfigService.getValue(Config.Sys.Team.最高等级);
        if (team.getRank() >= maxRank) {
            return R.fail("您已达到最高等级~");
        }

        //4.验证后台是否开启材料和普通升级，并验证用户是否有充足的热豆和扳手
        // 获取所有升级方案配置
        List<ConsumerTeamUpgradePlan> upgradePlans = this.consumerTeamUpgradePlanService.getConsumerTeamUpgradePlans();
        // 按升级方案配置转为map
        Map<ConsumerTeamUpgradePlanTypeEnum, ConsumerTeamUpgradePlan> upgradePlanMap = this.consumerTeamUpgradePlanService.getConsumerTeamUpgradePlanMap(upgradePlans);
        // 获取用户热豆和扳手余额[热豆余额，扳手余额]
        BigDecimal[] balance = this.consumerTeamUpgradePlanService.getHotBeansAndWrenchBalance(userId);
        // 是否开启材料升级
        boolean isOpenMaterialUpgrade = this.consumerTeamUpgradePlanService.isOpen(upgradePlans,ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_ONE);
        // 是否开启普通升级
        boolean isOpenOrdinaryUpgrade = this.consumerTeamUpgradePlanService.isOpen(upgradePlans,ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_THREE);

        //5.返回材料或普通升级详情
        // 只要满足一种队伍升级方式（材料||普通），就去 获取队伍（材料&普通）升级详情的公共VO
        TeamCommonUpgradesVo commonUpgradesVo = (isOpenMaterialUpgrade||isOpenOrdinaryUpgrade) ? this.getTeamCommonUpgradesVo(team,balance):null;
        //5.1 材料升级详情
        TeamMaterialUpgradesVo materialUpgradesVo = isOpenMaterialUpgrade ? getMaterialUpgradesVo(upgradePlanMap,commonUpgradesVo,userId):null;
        //5.2 普通升级详情
        TeamOrdinaryUpgradesVo ordinaryUpgradesVo = isOpenOrdinaryUpgrade ? getOrdinaryUpgradesVo(upgradePlanMap,commonUpgradesVo,userId):null;
        // 返回队伍升级详情
        return R.ok(TeamUpgradesVo.builder().commonUpgradesVo(commonUpgradesVo).materialUpgradesVo(materialUpgradesVo).ordinaryUpgradesVo(ordinaryUpgradesVo).build(),"获取队伍升级详情成功!");
    }



    /**
     * 队伍升级确认
     *
     * @param teamId 队伍id
     * @param upgradeType 队伍升级类型（0-材料升级；1-普通升级）
     * @param userId 用户id
     * @return 队伍升级确认
     */
    @Transactional(rollbackFor = Exception.class)
    public R<TeamUpgradesSuccessVo> upgradeConfirm(Long teamId,Integer upgradeType, Long userId) {
        //1.验证队伍升级开关
        Boolean isOpen = this.sysConfigService.getValue(Config.Sys.Team.升级开关);
        if (!isOpen) {
            return R.fail("升级维护中~");
        }
        //2.查询队伍信息
        ConsumerTeam team = this.consumerTeamService.getById(teamId);
        if (ObjUtil.isNull(team)) {
            return R.fail("未查询到队伍信息~");
        }
        //2.1 非队伍管理员不能进行升级
        if (!Objects.equals(userId,team.getCreateUserId())) {
            return R.fail("非队伍管理员（队长），不能进行队伍升级！");
        }
        //3.验证当前队伍等级是否为最高等级
        Integer maxRank = this.sysConfigService.getValue(Config.Sys.Team.最高等级);
        if (team.getRank() >= maxRank) {
            return R.fail("您已达到最高等级~");
        }

        // 获取所有升级方案配置
        List<ConsumerTeamUpgradePlan> upgradePlans = this.consumerTeamUpgradePlanService.getConsumerTeamUpgradePlans();
        // 按升级方案配置转为map
        Map<ConsumerTeamUpgradePlanTypeEnum, ConsumerTeamUpgradePlan> upgradePlanMap = this.consumerTeamUpgradePlanService.getConsumerTeamUpgradePlanMap(upgradePlans);
        // 获取用户热豆和扳手余额[热豆余额，扳手余额]
        BigDecimal[] balance = this.consumerTeamUpgradePlanService.getHotBeansAndWrenchBalance(userId);
        // 获取队伍（材料&普通）升级详情的公共VO
        TeamCommonUpgradesVo commonUpgradesVo = this.getTeamCommonUpgradesVo(team,balance);
        // 3.1.材料升级
        if (upgradeType==0) {
            return upgradeConfirmMaterial(getMaterialUpgradesVo(upgradePlanMap,commonUpgradesVo,userId));
        }
        // 3.2.普通升级
        if (upgradeType==1) {
            return upgradeConfirmOrdinary(getOrdinaryUpgradesVo(upgradePlanMap,commonUpgradesVo,userId));
        }
        return R.fail("该队伍升级方式不支持！");
    }

    /**
     * 主成员或队长退出队伍
     *
     * 只能主成员或队长退出队伍，退出队伍时解除主成员的家庭亲属关系
     *
     * 如果退出的是队长，需要选队伍中最先进入队伍的成员为队长；若没有成员则解散队伍
     *
     * @param teamId 队伍id
     * @param userId 当前用户id（需要判断在队伍中是否为主成员或队长）
     * @return String
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> quit(Integer teamId, Long userId) {

        //1.获取队伍信息
        ConsumerTeam consumerTeam = this.consumerTeamService.getById(teamId);
        if (ObjUtil.isNull(consumerTeam)) {
            return R.fail("队伍不存在!");
        }
        //2.如果当前退出成员为主成员，返回该主成员的家庭成员信息（包括主成员信息），执行退出队伍逻辑
        List<ConsumerTeamMemberVo> familyMembers = this.consumerTeamMemberService.isMainMemberReturnFamilyMembers(teamId,userId);
        if (CollUtil.isNotEmpty(familyMembers)) {
            //2.1 执行家庭成员（包括主成员）退出逻辑
            for (ConsumerTeamMemberVo member : familyMembers) {
                String message;
                InteractMsgEnum msgEnum;
                if (member.getType()== ConsumerTeamMemberTypeEnum.TYPE_MAIN) {
                    message = "已退出" + consumerTeam.getName() + "的队伍";
                    msgEnum = InteractMsgEnum.TEAM_QUIT;
                }else{
                    message = "主成员["+ SecurityUtils.getNickName() +"]已退出" + consumerTeam.getName() + "的队伍,亲属关系已解除";
                    msgEnum = InteractMsgEnum.TEAM_QUIT_SECURE;
                }
                this.consumerTeamService.actionLogoutTeam(
                        teamId,
                        member.getUserId(),
                        consumerTeam.getCreateUserId(),
                        message,
                        member.getNickName(),
                        msgEnum
                );
            }
        }
        //3.刷新队伍打卡次数
        consumerTeamService.refreshPunchGain(Long.valueOf(teamId));
        return R.ok_msg("退出队伍成功！");
    }

    /**
     * 主成员解除自己家庭亲属关系
     *
     * 只有每个家庭组长有权限解除
     *
     * @param teamId 队伍id
     * @param secureUserId 需要解除亲属关系的亲属用户id
     * @param userId 当前用户id（需要判断在队伍中是否为主成员）
     * @return String
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> secureRelationship(Integer teamId, Long secureUserId, Long userId) {
        //1.获取队伍信息
        ConsumerTeam consumerTeam = this.consumerTeamService.getById(teamId);
        if (ObjUtil.isNull(consumerTeam)) {
            return R.fail("队伍不存在!");
        }
        //2.获取要解除家庭亲属关系的家庭成员基本信息
        List<ConsumerTeamMemberVo> members = this.consumerTeamMemberService.isMainMemberReturnFamilyMember(teamId,secureUserId,userId);
        for (ConsumerTeamMemberVo member : members) {
            this.consumerTeamService.actionLogoutTeam(
                    teamId,
                    member.getUserId(),
                    consumerTeam.getCreateUserId(),
                    consumerTeam.getName() + "队伍中，"+"主成员["+ SecurityUtils.getNickName() +"]已解除和您的亲属关系",
                    member.getNickName(),
                    InteractMsgEnum.TEAM_QUIT_SECURE
            );
        }
        //3.刷新队伍打卡次数
        consumerTeamService.refreshPunchGain(Long.valueOf(teamId));
        return R.ok_msg("解除关系成功！");
    }


    /**
     * 队长踢人
     *
     * -队长可踢所有人，但组长被踢或主动退出后，该家庭的所有成员均会被踢出，且再次加入新队伍时，需要进行重新邀请
     *
     * @param teamId 队伍id
     * @param kickUserId 被踢用户的用户id
     * @param userId 当前用户id（需要判断在队伍中是否为队长）
     * @return String
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> kickingPeople(Integer teamId, Long kickUserId, Long userId) {
        //1.获取队伍信息
        ConsumerTeam consumerTeam = this.consumerTeamService.getById(teamId);
        if (ObjUtil.isNull(consumerTeam)) {
            return R.fail("队伍不存在!");
        }
        //2.判断要踢的人是主成员还是非主成员
        List<ConsumerTeamMemberVo> familyMembers = this.consumerTeamMemberService.isMainMemberReturnFamilyMemberOrMore(teamId,kickUserId,userId);
        if (CollUtil.isNotEmpty(familyMembers)) {
            //2.1 执行被踢人员（可能为主成员）退出队伍逻辑
            for (ConsumerTeamMemberVo member : familyMembers) {
                this.consumerTeamService.actionLogoutTeam(
                        teamId,
                        member.getUserId(),
                        consumerTeam.getCreateUserId(),
                        "已被踢出" + consumerTeam.getName() + "的队伍",
                        member.getNickName(),
                        InteractMsgEnum.TEAM_QUIT_KICK
                );
            }
        }
        //3.刷新队伍打卡次数
        consumerTeamService.refreshPunchGain(Long.valueOf(teamId));
        return R.ok_msg("踢出成员成功！");
    }

    //***************************上面是相关调用接口，下面是相关封装********************************

    /**
     * 2.判断当前队伍是否满足队伍的升级条件
     *
     * 1.判断当前用户是否为队长
     * 2.判断当前用户是否具备队伍升级条件（材料升级 或 普通升级）
     *
     * @param teamId 队伍id
     * @param userId 用户id
     * @param vo  vo
     * @return true-队伍满足升级条件,不满足升级的原因
     */
    public void isUpgrade(Long teamId,Long userId,ConsumerTeamDetailVo vo) {
        TeamUpgradeConditionVo conditionVo = new TeamUpgradeConditionVo();
        //1.验证升级开关有无开启，没有开启不能升级，提示：升级维护中~
        Boolean isOpen = this.sysConfigService.getValue(Config.Sys.Team.升级开关);
        if (!isOpen) {
            conditionVo.setIsUpgrade(Boolean.FALSE);
            conditionVo.setUnUpgradeReason("升级维护中~");
            vo.setUpgradeCondition(conditionVo);
            return ;
        }
        //2.查询队伍信息
        ConsumerTeamVo team = this.consumerTeamService.getCurrentUserTeamOrGetNull(teamId,userId);
        if(team==null){
            conditionVo.setIsUpgrade(Boolean.FALSE);
            conditionVo.setUnUpgradeReason("非队伍管理员~");
            vo.setUpgradeCondition(conditionVo);
            return;
        }
        //3.验证当前队伍等级是否为最高等级
        Integer maxRank = this.sysConfigService.getValue(Config.Sys.Team.最高等级);
        if (team.getRank().intValue() == maxRank.intValue()) {
            conditionVo.setIsUpgrade(Boolean.FALSE);
            conditionVo.setUnUpgradeReason("当前队伍等级已为最高等级~");
            vo.setUpgradeCondition(conditionVo);
            return ;
        }
        //4.判断当前用户是否具备队伍升级条件（材料升级 或 普通升级）
        List<ConsumerTeamUpgradePlan> upgradePlans = this.consumerTeamUpgradePlanService.getConsumerTeamUpgradePlans();
        //获取当前用户的热豆和扳手余额
        BigDecimal[] hotBeansAndWrenchBalance = this.consumerTeamUpgradePlanService.getHotBeansAndWrenchBalance(userId);
        //是否支持材料升级，并且满足升级所需要的热豆和扳手
        this.consumerTeamUpgradePlanService.isMaterialUpgrade(upgradePlans,team.getRank(),hotBeansAndWrenchBalance,userId,conditionVo);
        //是否支持普通升级，并且满足升级所需要的热豆和扳手
        this.consumerTeamUpgradePlanService.isOrdinaryUpgrade(upgradePlans,team,hotBeansAndWrenchBalance,conditionVo);
        //满足两者之一即可升级
        conditionVo.setIsUpgrade(conditionVo.getIsMaterialUpgrade()||conditionVo.getIsOrdinaryUpgrade());

        vo.setUpgradeCondition(conditionVo);
    }


    /**
     * 3.筛选出以主队员为首的对应家庭信息
     *
     * @param userId 用户id
     * @param vo vo
     */
    private void filterTeamMembers(Long userId, ConsumerTeamDetailVo vo) {
        List<SysUserTeamVo> teamMember = vo.getTeamMember();
        if (CollUtil.isNotEmpty(teamMember)) {
            //转换距离上次打卡多久
            long currentTime = System.currentTimeMillis();
            teamMember.forEach(t->{
                t.setRecentPunch(convert(t.getRecentPunchDate(),currentTime));
                t.setTotalPunchs(t.getPunchCount()*t.getPunchRatio());
            });
            // 主成员列表
            List<SysUserTeamVo> mainTeamNumbers = teamMember.stream().filter(t -> t.getType() == ConsumerTeamMemberTypeEnum.TYPE_MAIN).collect(Collectors.toList());
            // 给主成员添加家庭成员
            List<SysUserTeamVo> familyTeamNumbers;
            // 队伍总打卡次数
            Integer totalPunchsTeam = 0;
            for (SysUserTeamVo main : mainTeamNumbers) {

                familyTeamNumbers = teamMember.stream().filter(t -> Objects.equals(main.getUserId(), t.getParentId())).collect(Collectors.toList());
                //家庭成员当日已打卡总次数 = 所有家庭成员当日已打卡次数 + 主成员当日已打卡次数
                int totalPunch= familyTeamNumbers.stream().mapToInt(SysUserTeamVo::getTotalPunch).sum()+ main.getTotalPunch();
                //家庭成员当日需打卡总次数 = 单人当日可打卡次数*家庭成员人数
                int totalPunchs = familyTeamNumbers.stream().mapToInt(SysUserTeamVo::getTotalPunchs).sum()+ main.getTotalPunchs();
                totalPunchsTeam += totalPunchs;
                main.setTotalPunch(totalPunch);
                main.setTotalPunchs(totalPunchs);
                familyTeamNumbers.forEach(f->{f.setTotalPunch(totalPunch);f.setTotalPunchs(totalPunchs);});
                main.setFamilyMember(familyTeamNumbers);
            }
            vo.setTotalPunchs(totalPunchsTeam);
            //各家庭之间按照已打卡次数总和进行降序排序
            mainTeamNumbers = mainTeamNumbers.stream().sorted(Comparator.comparing(SysUserTeamVo::getTotalPunch,Comparator.nullsLast(Integer::compareTo)).reversed()).collect(Collectors.toList());
            //把当前登录用户所在家庭放在第一个
            List<SysUserTeamVo> members = new ArrayList<>();
            List<SysUserTeamVo> ms;
            Iterator<SysUserTeamVo> iterator = mainTeamNumbers.iterator();
            SysUserTeamVo m;
            SysUserTeamVo p = null;
            SysUserTeamVo q = null;
            boolean isOk = false;
            while (iterator.hasNext()) {
                m = iterator.next();
                ms = new ArrayList<>(m.getFamilyMember());
                ms.add(m);
                boolean isIteratorRemove = false;
                for (SysUserTeamVo v : ms) {
                    //1.当前登录用户是队长
                    if(Objects.equals(userId, v.getUserId()) && ConsumerTeamRoleEnum.ADMINISTRATOR == v.getRole()){
                        members.add(0,m);
                        iterator.remove();
                        members.addAll(mainTeamNumbers);
                        isOk = true;
                        break;
                    }else{
                        //2.当前登录用户不是队长，则把队长放第二个
                        if(Objects.equals(userId, v.getUserId())){
                            p = m;
                            if (!isIteratorRemove) {
                                iterator.remove();
                                isIteratorRemove = Boolean.TRUE;
                            }
                        }
                        else if(ConsumerTeamRoleEnum.ADMINISTRATOR == v.getRole()){
                            q = m;
                            if (!isIteratorRemove) {
                                iterator.remove();
                                isIteratorRemove = Boolean.TRUE;
                            }
                        }
                        if (p != null && q != null) {
                            if (Objects.equals(p,q)) {
                                members.add(0,p);
                            }else{
                                members.add(0,p);
                                members.add(1,q);
                            }
                            members.addAll(mainTeamNumbers);
                            isOk = true;
                            break;
                        }
                    }
                }
                if (isOk) {
                    break;
                }
            }

            vo.setTeamMember(members);
        }
    }

    /**
     * 4.获取 队伍打卡增益配置、队伍当前增益信息、队伍下阶段增益信息
     *
     * @param teamId 队伍id
     * @param userId 用户id
     * @param vo vo
     */
    private void obtainGain(Long teamId, Long userId, ConsumerTeamDetailVo vo) {
        //1.获取我在该队伍的今日打卡收益
        BigDecimal todayIncome = punchUserRecordService.getUserTotalIncome(userId, teamId, LocalDate.now());

        //2.队伍打卡增益配置
        List<ConsumerTeamGainRule> ruleList = teamGainRuleService.getConsumerTeamGainRules(vo.getRank());
        vo.setGainRuleList(ruleList);

        //3.队伍当前增益信息
        ConsumerTeamGainRule currentGain = ruleList.stream().filter(rule -> rule.getTotalPunch() <= vo.getTotalPunch()).reduce((first, second) -> second).orElse(null);
        if (currentGain != null) {
            TeamGainInfoVo gainVo = new TeamGainInfoVo();
            gainVo.setGain(currentGain.getGain());
            gainVo.setLackPunchCount(0);
            gainVo.setPunchCount(currentGain.getTotalPunch());
            gainVo.setIncome(todayIncome.multiply(gainVo.getGain()).setScale(2, RoundingMode.UP));
            vo.setCurrentGainInfo(gainVo);
        }

        //4.队伍下阶段增益信息
        ConsumerTeamGainRule nextGain = ruleList.stream().filter(rule -> rule.getTotalPunch() > vo.getTotalPunch()).findFirst().orElse(null);
        if (nextGain != null) {
            if (todayIncome.compareTo(BigDecimal.ZERO) <= 0) {
                // 用户今日在该队伍还没有收益，则以用户下次预估收益为基础去计算
                PunchUserConfig config = punchUserConfigService.getConfig(userId);
                if (config.getNftNumberId() != null) {
                    Equipment equipment = equipmentService.getByNftNumberId(config.getNftNumberId());
                    if (Objects.nonNull(equipment)) {
                        BigDecimal nextIncome = userPunchService.calcPunchIncome(equipment, true);
                        // 判断日收益上限
                        BigDecimal dayIncomeMax = sysConfigService.getValue(Config.Sys.Punch.日打卡收益上限);
                        if (Objects.nonNull(dayIncomeMax)) {
                            // 如果设置了打卡上限
                            if (LocalDateTimeUtil.isSameDay(config.getNextPunchTime(), LocalDateTime.now())) {
                                // 如果下次打卡是当天，则判断今日打卡是否达到上限
                                BigDecimal subIncome = dayIncomeMax.subtract(config.getTodayPunchIncome());
                                if (subIncome.compareTo(BigDecimal.ZERO) <= 0) {
                                    nextIncome = BigDecimal.ZERO;
                                } else {
                                    // 如果剩余的收益额度小于打卡收益额度，则以剩余收益额度为准
                                    if (subIncome.compareTo(nextIncome) < 0) {
                                        nextIncome = subIncome;
                                    }
                                }
                            } else {
                                // 如果不是同一天，则判断打卡收益是否超过打卡上限
                                if (dayIncomeMax.compareTo(nextIncome) < 0) {
                                    nextIncome = dayIncomeMax;
                                }
                            }
                        }
                        // 将下次预估收益设置到今日收益去计算
                        todayIncome = nextIncome;
                    }
                }
            }

            TeamGainInfoVo gainVo = new TeamGainInfoVo();
            gainVo.setGain(nextGain.getGain());
            gainVo.setPunchCount(nextGain.getTotalPunch());
            gainVo.setLackPunchCount(gainVo.getPunchCount() - vo.getTotalPunch());
            gainVo.setIncome(todayIncome.multiply(gainVo.getGain()).setScale(2, RoundingMode.UP));
            vo.setNextGainInfo(gainVo);
        }
    }

    /**
     * 队伍材料升级确认
     *
     * @param vo 材料升级信息
     * @return String
     */
    private R<TeamUpgradesSuccessVo> upgradeConfirmMaterial(TeamMaterialUpgradesVo vo) {
        if (!vo.getIsHaveNft()) {
            return R.fail("您还没有藏品|装备，前往市场逛逛吧~");
        }
        if (!vo.getIsHaveHotBeans()) {
            return R.fail("热豆余额不足~");
        }
        if (!vo.getIsHaveWrenchs()) {
            return R.fail("扳手余额不足~");
        }
        // 队伍材料升级销毁对应藏品|装备
        this.sysUserNftService.commonDestruction(vo.getUserId(), UserNftStatusEnum.TEAM_UPGRADE_BURN,vo.getNftNumberId());
        // 消耗热豆
        this.sysUserWalletService.reduce(vo.getUserId(), UserWalletEnum.INTEGRAL,vo.getConsumeHotBeans(), WalletChangeTypeEnum.TEAM_MATERIAL_PROMOTION,vo.getUserId(),"队伍材料升级消耗热豆");
        // 消耗扳手
        this.userAssetWrenchRecordService.updateWrenchBalance(vo.getUserId(), WalletWrenchChangeTypeEnum.TEAM_MATERIAL_PROMOTION, WalletChangeDirectionEnum.OUT,vo.getConsumeWrenchs(), vo.getUserId(), "队伍材料升级消耗扳手");
        // 修改队伍等级 和 最大成员数量
        this.consumerTeamService.updateRankAndMaxMember(vo.getTeamId(), vo.getUpgradeRank(), vo.getUpgradeNumber());

        return R.ok(TeamUpgradesSuccessVo.builder().rank(vo.getUpgradeRank()).number(vo.getUpgradeNumber()).gain(vo.getTeamGainRuleVo().getFourToGain()).build(), "队伍材料升级成功！");
    }

    /**
     * 队伍普通升级确认
     *
     * @param vo 普通升级信息
     * @return String
     */
    private R<TeamUpgradesSuccessVo> upgradeConfirmOrdinary(TeamOrdinaryUpgradesVo vo) {
        Map<Integer, TeamOrdinaryInfoVo> voMap = vo.getTeamOrdinaryInfoVos().stream().collect(Collectors.toMap(TeamOrdinaryInfoVo::getType, Function.identity()));
        if (!voMap.get(TeamOrdinaryInfoVo.Type.TEAM_NUMBER).getIsOk()) {
            return R.fail("队伍人数未达标~");
        }
        if (!voMap.get(TeamOrdinaryInfoVo.Type.GIFT_NUMBER).getIsOk()) {
            return R.fail("互送礼物未达标~");
        }
        if (!voMap.get(TeamOrdinaryInfoVo.Type.PUNCH_NUMBER).getIsOk()) {
            return R.fail("累计打卡未达标~");
        }
        if (!voMap.get(TeamOrdinaryInfoVo.Type.REAL_NAME).getIsOk()) {
            return R.fail("队伍实名未达标~");
        }
        if (!vo.getIsHaveHotBeans()) {
            return R.fail("热豆余额不足~");
        }
        if (!vo.getIsHaveWrenchs()) {
            return R.fail("扳手余额不足~");
        }
        // 消耗热豆
        this.sysUserWalletService.reduce(vo.getUserId(), UserWalletEnum.INTEGRAL,vo.getConsumeHotBeans(), WalletChangeTypeEnum.TEAM_ORDINARY_PROMOTION,vo.getUserId(),"队伍普通升级消耗热豆");
        // 消耗扳手
        this.userAssetWrenchRecordService.updateWrenchBalance(vo.getUserId(), WalletWrenchChangeTypeEnum.TEAM_ORDINARY_PROMOTION, WalletChangeDirectionEnum.OUT,vo.getConsumeWrenchs(), vo.getUserId(), "队伍普通升级消耗扳手");
        // 修改队伍等级 和 最大成员数量
        this.consumerTeamService.updateRankAndMaxMember(vo.getTeamId(), vo.getUpgradeRank(), vo.getUpgradeNumber());
        return R.ok(TeamUpgradesSuccessVo.builder().rank(vo.getUpgradeRank()).number(vo.getUpgradeNumber()).gain(vo.getTeamGainRuleVo().getFourToGain()).build(), "队伍普通升级成功！");
    }


    /**
     * 获取材料升级详情
     * @param upgradePlanMap 升级方案
     * @param commonUpgradesVo （材料&普通）升级详情的公共VO
     * @param userId 用户id
     * @return 材料升级详情
     */
    private TeamMaterialUpgradesVo getMaterialUpgradesVo(Map<ConsumerTeamUpgradePlanTypeEnum, ConsumerTeamUpgradePlan> upgradePlanMap,
                                                         TeamCommonUpgradesVo commonUpgradesVo,
                                                         Long userId) {
        // 获取藏品/装备id
        Long nftId = upgradePlanMap.get(ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_ONE).getNftId();
        if (nftId==null) {
            throw new ServiceException("藏品/装备id不存在，请检查升级方案配置！");
        }
        // 判断队长有无藏品/装备，则返回对应的藏品/装备id；如果有多个返回最先拥有的藏品/装备
        Long nftNumberId = this.sysUserNftService.getAnyNftNumber(nftId, userId);
        // 藏品/装备数量
        long nftNumberCounts = nftNumberId==null?0:this.sysUserNftService.getNftNumberCounts(nftId, userId);
        // 根据 材料藏品/装备id 获取 藏品/装备主图地址
        String nftImgPath = this.nftService.getNftMainImg(nftId);
        // 需要消耗的热豆和扳手数量
        int currentRank = commonUpgradesVo.getCurrentRank();
        BigDecimal consumeHotBeans = this.consumerTeamUpgradePlanService.countConsumeHotBeansOrWrenchs(currentRank,upgradePlanMap.get(ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_ONE));//材料升级二级热豆消耗
        BigDecimal consumeWrenchs = this.consumerTeamUpgradePlanService.countConsumeHotBeansOrWrenchs(currentRank,upgradePlanMap.get(ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_TWO));//材料升级二级扳手消耗
        // 封装材料升级详情
        TeamMaterialUpgradesVo materialUpgradesVo = TeamMaterialUpgradesVo.builder()
                .isHaveNft(nftNumberCounts != 0)
                .isHaveHotBeans(commonUpgradesVo.getHotBeansBalance().compareTo(consumeHotBeans)>=0)
                .isHaveWrenchs(commonUpgradesVo.getWrenchsBalance().compareTo(consumeWrenchs)>=0)
                .nftNumberId(nftNumberId)
                .nftNumberCounts(nftNumberCounts)
                .nftImgPath(nftImgPath)
                .consumeHotBeans(consumeHotBeans)
                .consumeWrenchs(consumeWrenchs)
                .userId(userId)
                .build();
        BeanUtils.copyBeanProp(materialUpgradesVo,commonUpgradesVo);
        return materialUpgradesVo;
    }

    /**
     * 获取普通升级详情
     * @param upgradePlanMap 升级方案
     * @param commonUpgradesVo （材料&普通）升级详情的公共VO
     * @param userId 用户id
     * @return 普通升级详情
     */
    private TeamOrdinaryUpgradesVo getOrdinaryUpgradesVo(Map<ConsumerTeamUpgradePlanTypeEnum, ConsumerTeamUpgradePlan> upgradePlanMap,
                                                         TeamCommonUpgradesVo commonUpgradesVo,
                                                         Long userId) {
        // 获取队伍普通升级条件
        List<TeamOrdinaryInfoVo> teamOrdinaryInfoVos = this.getTeamOrdinaryInfoVos(upgradePlanMap,commonUpgradesVo);
        // 需要消耗的热豆和扳手数量
        int currentRank = commonUpgradesVo.getCurrentRank();
        BigDecimal consumeHotBeans = this.consumerTeamUpgradePlanService.countConsumeHotBeansOrWrenchs(currentRank,upgradePlanMap.get(ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_FIVE));//普通升级二级热豆消耗
        BigDecimal consumeWrenchs = this.consumerTeamUpgradePlanService.countConsumeHotBeansOrWrenchs(currentRank,upgradePlanMap.get(ConsumerTeamUpgradePlanTypeEnum.CONSUME_TYPE_SEX));//普通升级二级扳手消耗
        // 封装普通升级详情
        TeamOrdinaryUpgradesVo ordinaryUpgradesVo = TeamOrdinaryUpgradesVo.builder()
                .isHaveHotBeans(commonUpgradesVo.getHotBeansBalance().compareTo(consumeHotBeans)>=0)
                .isHaveWrenchs(commonUpgradesVo.getWrenchsBalance().compareTo(consumeWrenchs)>=0)
                .teamOrdinaryInfoVos(teamOrdinaryInfoVos)
                .consumeHotBeans(consumeHotBeans)
                .consumeWrenchs(consumeWrenchs)
                .userId(userId)
                .build();
        BeanUtils.copyBeanProp(ordinaryUpgradesVo,commonUpgradesVo);
        return ordinaryUpgradesVo;
    }


    /**
     * 获取队伍（材料&普通）升级详情的公共VO
     *
     * @param team 当前的队伍信息
     * @param balance 用户热豆和扳手余额[热豆余额，扳手余额]
     * @return 普通升级详情
     */
    private TeamCommonUpgradesVo getTeamCommonUpgradesVo(ConsumerTeam team,
                                                         BigDecimal[] balance) {
        int currentRank = team.getRank();
        int currentNumber = team.getMaxNumber();
        // 获取全队热豆增幅梯度
        TeamGainRuleVo teamGainRuleVo = this.consumerTeamGainRuleService.getTeamGainRuleVo(currentRank);
        // 获取队伍升级后的最大成员数量
        Integer value = this.sysConfigService.getValue(Config.Sys.Team.人员扩充幅度);
        int upgradeNumber = currentNumber+value;
        // 返回材料升级详情
        return TeamCommonUpgradesVo.builder()
                .teamId(Long.valueOf(team.getId()))
                .hotBeansBalance(balance[0])
                .wrenchsBalance(balance[1])
                .currentRank(currentRank)
                .upgradeRank(currentRank+1)
                .teamGainRuleVo(teamGainRuleVo)
                .currentNumber(currentNumber)
                .hasNumber(team.getMemberCount())
                .upgradeNumber(upgradeNumber)
                .build();
    }

    /**
     * 获取队伍普通升级条件
     *
     * @param map 升级方案
     * @param vo （材料&普通）升级详情的公共VO
     * @return list
     */
    private List<TeamOrdinaryInfoVo> getTeamOrdinaryInfoVos(Map<ConsumerTeamUpgradePlanTypeEnum, ConsumerTeamUpgradePlan> map,
                                                            TeamCommonUpgradesVo vo) {
        // 队伍id
        long teamId = vo.getTeamId();
        // 队伍等级
        int teamRank = vo.getCurrentRank();
        // 队伍已有人数
        int hasNumber = vo.getHasNumber();
        // 队伍人数上线
        int currentNumber = vo.getCurrentNumber();

        return this.consumerTeamUpgradePlanService.getTeamOrdinaryInfoVos(map,teamId,teamRank,hasNumber,currentNumber);
    }

    /**
     * xx分钟前打卡~：选中角色后显示，显示对应角色的打卡状态，刚刚打卡、1分钟前打卡、1小时前打卡、今天还未开始打卡（超过24小时未打卡），数值计整数，不足取舍
     *
     * 0_刚刚打卡~；1_1分钟前打卡~;2_1小时前打卡~;3_今天还未开始打卡~
     *
     * @param recentPunchDate 今日最近打卡时间
     * @param currentTime 当前时间戳
     * @return
     */
    private String convert(Date recentPunchDate,long currentTime) {
        if (recentPunchDate!=null) {
            long recentPunchTime = recentPunchDate.getTime();
            long diff = ((currentTime - recentPunchTime) / 1000);
            if(diff <= 60){
                return "刚刚打卡";
            }else if(diff <= 60*60){
                return diff/60+"分钟前打卡";
            }else {
                return diff/(60*60)+"小时前打卡";
            }
        }else{
            return "今天还未开始打卡";
        }
    }

}

